package de.wiroo.setup.web.common;

import java.io.Serializable;

import org.apache.wicket.model.IModel;

import de.stefanocke.roo.wicket.RooStereotype;
import de.wiroo.setup.domain.common.Identifiable;

/**
 * The base class for all Entity models.
 */
@RooStereotype("wicket.EntityModel")
@SuppressWarnings("serial")
public abstract class EntityModel<E, ID extends Serializable> implements IModel<E> {

	private ID id;

	private transient E instance;

	/**
	 * Workaround for ITD base code gen, since it seems not to handle generic
	 * arguments in constructors properly.
	 * 
	 * @param id
	 * @param entity
	 */
	@SuppressWarnings("unchecked")
	public EntityModel(Object id, Object entity) {
		this((ID) id, (E) entity);
	}

	public EntityModel() {
	}

	public EntityModel(ID id, E entity) { // Should be 2 separate constructors,
											// but then we get into type erasure
											// trouble
		if (id != null) {
			this.id = id;
		} else if (entity != null) {

			this.id = getId(entity);
			instance = entity;
		} else {
			// throw new
			// IllegalArgumentException("Exactly one of id or entity must be non-null.");
		}
	}

	public ID getId() {
		return id;
	}

	@SuppressWarnings("unchecked")
	protected ID getId(E entity) {
		if (entity instanceof Identifiable) {
			return ((Identifiable<ID>) entity).getId();
		} else {
			throw new UnsupportedOperationException("The entity " + entity.getClass()
					+ " does not implement Identifiable. " + "Please override getId Method in subclass.");
		}
	}

	public E getObject() {
		if (instance == null) {
			if (id != null) {
				instance = load(id);
			}
		}
		return instance;
	}

	protected abstract E load(ID id);

	public void setObject(E object) {
		if (object == null) {
			id = null;
		} else {
			id = getId(object);
		}
		instance = object;
	}

	public void detach() {
		if (instance != null && getId(instance) != null) {
			this.id = getId(instance);
		}
		if (this.id != null) {
			instance = null;
		}
	}
}
